package com.AdvancedDemos.动态代理;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 为什么要代理?
 * 对象如果嫌身上干的事太多的话，可以通过代理来转移部分职责。
 * <p>
 * 动态代理必须基于接口
 */
public class 动态代理 {
    public static void main(String[] args) {
        /*准备被代理接口的实现类*/
        BigStar bs = new BigStar("杨超越");
//        bs.setName("杨超越");

        /*准备生成代理对象所需要的参数*/
        /*java.lang.reflect.Proxy类提供了为对象产生代理对象的静态方法:
        public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) , 该方法返回代理对象,有3个参数*/
        //参数1  类加载器(固定写法),用于指定用哪个类加载器,去加载生成的代理对象
        ClassLoader classLoader = 动态代理.class.getClassLoader();
        //参数2  需要代理对象代理的接口们,这些接口用于指定生成的代理对象长什么样，也就是有哪些方法
        Class[] interfaces = new Class[]{Star.class};
        //参数3  代理对象调用被代理的接口中的函数时使用的"调用处理器",当中写明真正的代理过程,即用来指定生成的代理对象在调被代理的接口中的函数时要干什么事情,通常是对被代理接口中函数功能的扩展,即在执行函数前进行一些操作,在函数执行后进行一些操作
        InvocationHandler invocationHandler = new InvocationHandler() {
            /**
             * @param proxy  代表代理者本身，基本不使用，忽略
             * @param method 每次调用代理的方法时,invoke都会被执行一次,参数method指代的就是本次被代理的方法
             * @param args   代表当前正在代理的方法的实参
             * @return invoke返回的是代理方法执行被代理接口中的函数的返回值
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //定义了代理过程
                String methodName = method.getName();
                System.out.println("代理" + methodName + "开始");
                if ("sing".equals(methodName)) {
                    System.out.println("前增强：准备话筒");
                }
                if ("dance".equals(methodName)) {
                    System.out.println("前增强：准备场地");
                }
                //正常调用被代理方法
                Object invokeResult = method.invoke(bs, args);

                System.out.println("后增强：收钱");
                System.out.println("代理" + methodName + "结束");
                return invokeResult;
            }
        };


        /*生成代理对象*/
        Star starProxy = (Star) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);//Proxy.newProxyInstance返回的是Object类型,需要强转成被代理的接口类型才能使用代理对象调用被代理的接口中的方法

        /*使用代理对象调用方法*/
        String singSong = starProxy.sing("大碗宽面");
        System.out.println(singSong);
        starProxy.dance();
    }
}
